-- UITalentMain
-- Create by zouyb
-- 天赋界面

require "game/ui/form/talent/UITalentFruitBonus"
require "game/ui/form/talent/UISlimeInfo"
require "game/ui/form/hero/UIHeroMain"
require "game/ui/form/training/UITrainingMain"
require "game/ui/form/training/UIUnlockRoom"
require "game/ui/form/sign_in/UISignIn"

--UITalentMain继承自Layer
UITalentMain = class("UITalentMain", function()
    return cc.Layer:create();
end);

function UITalentMain.create()
    return UITalentMain.new();
end

-- 英雄模型修正比例
local MODEL_FIX_SCALE = 0.8;

-- 每张背景可容纳的冈布奥数量
local PER_PAGE_SLIME_COUNT = 6;

-- 随机发言框存在的时长
local WORDS_FRAME_EXIST_TIME = 20;

-- 内部函数声明
local resize;

-- 道具数量变化缓存
local itemUpdateQueue = {};

-- 构造函数
function UITalentMain:ctor()
    self:setName("UITalentMain");

    local node = cc.CSLoader:createNode("layout/talent/TalentMain.csb");
    self:addChild(node);
    self.node = node;

    -- 初始冈布奥的层级(底层的冈布奥层级高点，随机发言才不被遮挡)
    self.petLocZOrder = 10000;

    -- 初始化
    self:init();

    local titleBg = findChildByName(node, "TOP/title_bg");
    titleBg:setVisible(false);

    -- 保存背景层的初始位置
    local middle = findChildByName(self.node, "MIDDLE");
    local middlePosY = middle:getPositionY();

    local far = findChildByName(self.node, "FAR");
    local farPosY = far:getPositionY();

    local sky = findChildByName(self.node, "SKY");
    local skyPosY = sky:getPositionY();

    self.layerPosYList = { middlePosY, farPosY, skyPosY };

    -- 注册点击回调处理
    self:registerTouchEvent();

    -- 注册事件回调
    self:registerEventCallback();

    -- 适配
    resize(self);

    -- 记录当前打开页面发言ID（发言不可重复）
    self.wordsIds = {};

    -- 处理下部按钮飞入效果
    self:setInit();
end

-- 注册点击回调处理
function UITalentMain:registerTouchEvent()
    -- 注册返回按钮点击事件
    local btnBack = findChildByName(self.node, "BT/btn_back");
    local function onBackClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            -- 关闭当前界面
            AudioM.playFx("button_return");

            -- 可能加载中未关闭
            -- 去掉加载动画
            closeCommunicatingForm();
            UIMgr.getCurrentScene():removeFormByName("UITalentMain");
        end
    end
    AddBackEventHandler(self, btnBack, onBackClick);
    TextStyleM.setTextStyle(btnBack, TextStyleM.TEXT_SIZE_MIDDLE, TextStyleM.TEXT_COLOR_BROWN_YELLOW, true);
    btnBack:setTitleText(getLocStr("btn_text_back"));

    local btnDesc = findChildByName(self.node, "BT/btn_desc");
    local function onDescClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            AudioM.playFx("button_click");

            -- 打开图鉴界面
            local uiSlimeInfo = UISlimeInfo.create();
            UIMgr:getCurrentScene():addForm(uiSlimeInfo);
            AudioM.playFx("ui_open1");
        end
    end
    btnDesc:addTouchEventListener(onDescClick);
    TextStyleM.setTextStyle(btnDesc, TextStyleM.TEXT_SIZE_MIDDLE, TextStyleM.TEXT_COLOR_BROWN_YELLOW, true);
    btnDesc:setTitleText(getLocStr("slimes_handbook"));

    -- 世界树果实奖励按钮
    local btnFruit = findChildByName(self.node, "BT/btn_award");
    local function onFruitClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            AudioM.playFx("button_click");
            -- 打开果实领取界面
            local uiBonus = UITalentFruitBonus.create();
            AudioM.playFx("button_award");
            UIMgr:getCurrentScene():addForm(uiBonus);
        end
    end
    btnFruit:addTouchEventListener(onFruitClick);
    TextStyleM.setTextStyle(btnFruit, TextStyleM.TEXT_SIZE_MIDDLE, TextStyleM.TEXT_COLOR_BROWN_YELLOW, true);
    btnFruit:setTitleText(getLocStr("btn_text_award"));

    local container = findChildByName(self.node, "container");
    container:addEventListener(function (sender, eventType)
        if eventType == ccui.ScrollviewEventType.scrolling or
            eventType == ccui.ScrollviewEventType.bounceTop or
            eventType == ccui.ScrollviewEventType.bounceBottom then
            -- 前景滑动时，处理近景、中景和远景的移动
            self:handleBackgroundScroll();

            self:checkEffects();

        end
    end);

    -- 打开训练室界面
    local function openTrainingRoom()
        local uiForm = UITrainingMain.create();
        UIMgr.getCurrentScene():addForm(uiForm);
    end

    -- 树洞训练室点击处理
    self.trainingRoom = findChildByName(self.node, "container/tree_root/training_room");
    self.trainingRoom:addTouchEventListener(function (sender, eventType)
        if eventType == ccui.TouchEventType.ended then
           openTrainingRoom();
           AudioM.playFx("workshop_open_ui");
        end
    end);

    -- 泡泡点击处理
    local bubbleBg = findChildByName(self.node, "container/tree_root/bubble/bg");
    bubbleBg:addTouchEventListener(function (sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            AudioM.playFx("button_tiny");
            if FeatureItemM.isFeatureOpen("heart") and FeatureItemM.isFeatureNeedReminder("heart") then
                local uiForm = UIUnlockRoom.create();
                UIMgr.getCurrentScene():addForm(uiForm);
            else
                openTrainingRoom();
            end
        end
    end);

    -- 检查训练室显示
    self:checkTrainingRoom();

    -- 公告栏
    local function onClickNoticeBoard(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            if DungeonAreaM.isDungeonPassed(ME.user, 11) then
                -- 通关1-1才开启该功能
                local uiForm = UISignIn.create(sender:getTag());
                UIMgr.getCurrentScene():addForm(uiForm);
                AudioM.playFx("ui_open1");
            end
        end
    end
    self.noticeBoard = findChildByName(self.node, "container/tree_root/notice_board");
    self.noticeBoard:addTouchEventListener(onClickNoticeBoard);

    local noticeBoardBuddle = findChildByName(self.node, "container/tree_root/notice_board/notice_board_bubble");
    local bgImg = findChildByName(noticeBoardBuddle, "bg");
    bgImg:addTouchEventListener(onClickNoticeBoard);

    -- 检查签到显示
    self:checkNoticeBoard();
end

function UITalentMain:registerEventCallback()
    -- 注册获得冈布奥的回调
    EventMgr.register("UITalentMain", event.GAIN_PET, function(classId)
        -- 重绘界面
        self:showSlimes();
    end);

    -- 注册英雄升阶的处理函数
    EventMgr.register("UITalentMain", event.UPGRADE_PET_RANK, function(args)
        -- self:showSlimes();
        self:updateItem(args["class_id"]);
        self:checkTrainingRoom();
    end)

    -- 注册英雄开始升阶的回调
    EventMgr.register("UITalentMain", event.SELECT_PET_UPGRADE, function(args)
        self:updateItem(args["class_id"]);
        self:checkTrainingRoom();
    end)

    -- 注册英雄升级的回调
    EventMgr.register("UITalentMain", event.PET_LEVEL_UP, function(args)
        self:updateItem(args["class_id"]);
        --self:showSlimes();
    end);

    -- 注册英雄佩戴武器徽章的回调
    EventMgr.register("UITalentMain", event.EQUIP_MEDAL_OR_WEAPON, function(args)
        self:updateItem(args["petId"]);
        self:checkTrainingRoom();
    end);

    EventMgr.register("UITalentMain", event.WS_STRENGTHEN_INFO, function()
        -- 检查训练室显示
        self:checkTrainingRoom();
    end);

    -- 注册事件
    EventMgr.register("UITalentMain", event.TRAINING_INFO_UPDATED, function()
        -- 检查训练室显示
        self:checkTrainingRoom();
    end);

    EventMgr.register("UITalentMain", event.SIGN_IN_BONUS, function()
        -- 检查签到显示
        self:checkNoticeBoard();
    end);

    -- 注册事件
    EventMgr.register("UITalentMain", event.ITEM_COUNT_UPDATED, function(classId)
        -- 如果已经失去了焦点
        if self.loseFocus then
            -- 先缓存起来
            itemUpdateQueue[classId] = true;

            return;
        end

        self:whenItemUpdated(classId);
    end);

    -- 注册英雄穿上时装的回调
    EventMgr.register("UITalentMain", event.EQUIP, function(args)
        local type = EquipM.query(args, "type");
        local petId = EquipM.query(args, "dbase")["pet_id"];
        if type == EQUIP_TYPE_COSTUME and petId ~= nil then
            self:updateHeadIcon(petId);
        end
    end);

    -- 注册英雄穿上时装的回调
    EventMgr.register("UITalentMain", event.UNEQUIP, function(args)
        local type = args.type;
        local petId = EquipM.query(args.classId, "dbase")["pet_id"];
        if type == EQUIP_TYPE_COSTUME and petId ~= nil then
            self:updateHeadIcon(petId);
        end
    end);

    -- 注册获得焦点的回调
    EventMgr.register("UITalentMain", event.FOCUS_CHANGED, function(para)
        if para["get"] == "UITalentMain" then
            UIMgr:getCurrentScene().topMenu:show(TOPMENU_FRAME_TREE, TOPMENU_FRAME_FRUIT);

            self:showButtons();

            -- 显示冈布奥模型
            self:setSlimeVisible(true);

            -- 标记是否焦点
            self.loseFocus = false;

            for classId, _ in pairs(itemUpdateQueue) do
                self:whenItemUpdated(classId);
            end
            itemUpdateQueue = {};
        else
            -- 失去焦点
            self.loseFocus = true;
        end

        -- 非初始化获得焦点，BOTTOM向上
        if para["get"] == "UITalentMain" and (para["lose"] ~= "UITalentMain" and para["lose"] ~= "UICommunicating")then
            if self.flying == false then
                self:btFlyUp();
            end
        end

    end);

    self:registerScriptHandler(function(ev)
        if ev == "exit" then
            EventMgr.removeAll("UITalentMain");

            if UIMgr.getCurrentScene().mainMap then
                UIMgr.getCurrentScene().mainMap:show();
            end

            UIMgr.tryClearUnusedTextures();
        elseif ev == "enter" then
            if UIMgr.getCurrentScene().mainMap then
                UIMgr.getCurrentScene().mainMap:hide();
            end

            -- 依次检查是否有提醒
            self:checkReminder();

            -- 播放界面音效
            AudioM.playFx("slime_open");
        elseif ev == "enterTransitionFinish" then
            -- 默认显示地图最底部内容
            local container = findChildByName(self.node, "container");
            container:setBounceBoundaryY(150);
            container:jumpToBottom();
        end
    end);
end

-- 物品数量变化回调
function UITalentMain:whenItemUpdated(classId)
    local type = ItemM.query(classId, "type");
    if type == ITEM_TYPE_FOOD or type == ITEM_TYPE_MATERIAL then
        if self.checkFeedNode == nil then
            self.checkFeedNode = cc.Node:create();
            self:addChild(self.checkFeedNode);
        end

        local function delay()
            -- 检查是否可喂食
            self:checkFeedStatus();
        end
        self.checkFeedNode:stopAllActions();
        performWithDelay(self.checkFeedNode, delay, 0.1);
    end
end

-- 依次检查是否有提醒
function UITalentMain:checkReminder()
    local checkList = { "food", "food2", "food3", "food4", "heart", "branch3", "branch4", "branch5" };
    local bt = findChildByName(self.node, "BT");
    bt:setVisible(true);
    local function callback(desc)
        local function okFunc()
            -- 继续检查是否还有其他提醒
            self:checkReminder();
        end
        self:hideBT();
        confirm2(CONFIRM_TYPE_OK, getLocStr("slime_note"), desc, okFunc);
    end

    -- 检查进度
    if not self.checkProgress then
        self.checkProgress = 1;
    end

    print("self.checkProgress:"..self.checkProgress);
    for i = self.checkProgress, #checkList do
        local ret = FeatureItemM.checkReminder(checkList[i], callback);
        if ret == true then
            -- 每次检查一个提醒，确认后再继续
            self.checkProgress = i + 1;
            return;
        end
    end

    -- 冈布奥宝宝
    local babyId = FateChainM.getFateBabyTip();
    if babyId > 0 then
        local function okFunc()
            -- 继续检查是否还有其他提醒
            self:checkReminder();
        end
        local info = FateChainM.queryBaby(babyId);
        local motherName = PetM.query(info["mother"], "short_name");
        local desc = string.format(getLocStr("gumball_baby_birth"), motherName);
        confirm2(CONFIRM_TYPE_OK, getLocStr("slime_note"), desc, okFunc);
        FateChainM.setFateChainBonusTip(babyId);
    end
end

-- 检查是否可喂食
function UITalentMain:checkFeedStatus()
    print("check feed status");
    Profiler.funcBegin("UITalentMain:checkFeedStatus");

    local heroList = ME.user:getAllPetIds();
    local checkTable = { "medal", "weapon", "food", };
    local canOperate = false;

    -- 更新UI
    for _, heroId in pairs(heroList) do
        -- 检查是否需要更新
        for _, v in pairs(checkTable) do
            if v == "food" then
                canOperate = PetM.canFeed(heroId);
            else
                canOperate = PetM.canOperate(heroId, v);
            end

            if canOperate == true then
                -- 已经有需要更新的内容
                break;
            end
        end

        if canOperate then
            -- 更新喂食图标
            self:updateItem(heroId);
        else
            -- 隐藏喂食图标
            self:hideFoodNode(heroId);
        end
    end

    Profiler.funcEnd("UITalentMain:checkFeedStatus");
end

-- 天赋图标初始化
function UITalentMain:initItem(child, classId)
    local bg = child:getChildByName("bg");
    local starNode = findChildByName(child, "star_node");
    if classId == nil then
        return;
    end

    -- 根据天赋获取冈布奥
    child:setTag(classId);
    self.childList[classId] = child;

    -- 更新星星和喂食图标
    self:updateItem(classId);

    -- 显示冈布奥模型
    local headImg = child:getChildByName("icon");
    local modelNode = child:getChildByName("model_node");
    local pet = PetM.getMyPet(classId);
    local modelId = pet:getModelId();
    local scale = MODEL_FIX_SCALE * PetM.queryModel(modelId, "model_scale");
    local offsetX = PetM.queryModel(modelId, "offset_x");
    local offsetY = PetM.queryModel(modelId, "offset_y");
    local iconPath = getHeroIconPath(PetM.queryModel(modelId, "icon"));
    local modelAnim = createHeroModel(modelId, headImg, modelNode, scale, iconPath, offsetX, offsetY, true);

    -- 播放呼吸动画
    playSlightAnima(headImg);

    local foodNode = findChildByName(child, "food_node");

    -- 如果升阶中，则隐藏树上的图标
    if TrainingM.isUpgrading(classId) or
        AwakeM.isCarving(classId) then
        bg:setVisible(false);
        headImg:setVisible(false);
        modelNode:setVisible(false);
        starNode:setVisible(false);
        foodNode:setVisible(false);
    end

    -- 如果喂食图标可见
    if modelAnim ~= nil and foodNode:isVisible() then
        local function playAtkAnim()
            local rand = math.random(1, 2);
            local heroDisplay = findChildByName(modelNode, "HeroDisplay");

            if rand == 1 then
                -- 播放施法动作
                heroDisplay:switchAction(HERO_ACTION_CAST_ME);
            else
                -- 播放攻击动作
                heroDisplay:switchAction(HERO_ACTION_ATTACK);
            end
        end

        -- 延迟播放攻击动作
        local delayFix = math.random(1, 5) / 5;
        performWithDelay(child, playAtkAnim, delayFix);
    end

    -- 点击回调处理
    bg:setTag(classId);
    bg:setTouchEnabled(true);
    bg:addTouchEventListener(function(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            -- 打开英雄主界面
            local classId = sender:getTag();
            UIMgr.getCurrentScene():removeFormByName("UIHeroMain");
            local uiHeroMain = UIHeroMain.create(classId);
            UIMgr.getCurrentScene():addForm(uiHeroMain);
            AudioM.playFx("ui_open1");
        end
    end);

    -- 检查随机发言
    local canTalk = self:checkCanTalk(classId);
    if canTalk then
        local sceneType = 3;
        local wordsId = FormulaM.invoke("CALC_RANDOM_WORDS", sceneType, classId, self.wordsIds);
        if not wordsId then
            return;
        end

        table.insert(self.wordsIds, wordsId)

        local content = RandomWordsM.query(sceneType, wordsId, "content");

        -- 创建发言框
        local wordsNode = findChildByName(child, "words_node");
        wordsNode:removeAllChildren();
        local delayAppearTime = math.random(5)*0.3;
        createWordsFrame(wordsNode, content, WORDS_FRAME_ARROW_BOTTOM_RIGHT, WORDS_FRAME_EXIST_TIME, delayAppearTime);
    end
end

-- 更新星星和喂食图标
function UITalentMain:updateItem(classId)
    local child = self.childList[classId];
    if not child then
        return;
    end

    -- 如果升阶中，则隐藏树上的图标
    local bg = child:getChildByName("bg");
    local starNode = findChildByName(child, "star_node");
    local headImg = child:getChildByName("icon");
    local modelNode = child:getChildByName("model_node");
    local foodNode = findChildByName(child, "food_node");

    if TrainingM.isUpgrading(classId) or
        AwakeM.isCarving(classId) then
        bg:setVisible(false);
        headImg:setVisible(false);
        modelNode:setVisible(false);
        starNode:setVisible(false);
        foodNode:setVisible(false);
    else
        bg:setVisible(true);
        headImg:setVisible(true);
        modelNode:setVisible(true);
        starNode:setVisible(true);
        foodNode:setVisible(true);
    end

    -- 星级
    local starNode = findChildByName(child, "star_node");

    local pet = PetM.getMyPet(classId);
    local rank = pet:getRank();

    local starPath = "images/ui/symbol/star_little.png";

    -- 如果冈布奥已觉醒，使用橙色的星星
    if AwakeM.isCarved(classId) then
        starPath = "images/ui/symbol/star_little3.png";
    end

    -- 显示星星
    local tStar = createStarLayer(rank, starPath, "center", -0.6);
    if tStar ~= nil then
        starNode:removeAllChildren();
        starNode:addChild(tStar);
        starNode:setScale(0.7);
    end

    -- 如果还可以喂食，显示提示图标
    local foodNode = findChildByName(child, "food_node");
    local checkTable = { "medal", "weapon", "food", };
    local iconPath;
    local foodImg = findChildByName(foodNode, "food");

    local scale = 1;
    for _, v in pairs(checkTable) do
        -- 能否操作
        local canOperate = false;
        if v == "food" then
            canOperate = PetM.canFeed(classId);
        else
            canOperate = PetM.canOperate(classId, v);
        end

        if canOperate == true then
            local foodBg = findChildByName(foodNode, "dialog");
            foodNode:setTouchEnabled(true);
--            foodBg:setOpacity(0.88 * 255);
--            foodImg:setOpacity(0.8 * 255);

            -- 轻微收缩
            -- playBubbleSlightAnima(foodNode, -0.12);

            foodBg:addTouchEventListener(function(sender, eventType)
                if eventType == ccui.TouchEventType.ended then
                    AudioM.playFx("button_click");
                    -- 打开英雄主界面
                    UIMgr.getCurrentScene():removeFormByName("UIHeroMain");
                    local uiHeroMain = UIHeroMain.create(classId);
                    UIMgr.getCurrentScene():addForm(uiHeroMain);
                end
            end);

            if v == "upgrade" then
                iconPath = "images/ui/talent/upgrade.png";
            elseif v == "weapon" then
                local weaponClassId = PetM.getWeapon(classId);
                if weaponClassId then
                    local iconIndex = ItemM.query(weaponClassId, "icon");
                    if haveItemSmallIcon(iconIndex) then
                        iconPath = getItemSmallIconPath(iconIndex);
                    else
                        iconPath = getItemIconPath(iconIndex);
                        scale = 0.3;
                    end
                end
            elseif v == "medal" then
                local medalClassId = PetM.getMedal(classId);
                if medalClassId then
                    local iconIndex = ItemM.query(medalClassId, "icon");
                    iconPath = getItemSmallIconPath(iconIndex);
                end
            else
                iconPath = getSmallAttribIconPath("food");
                scale = 0.9;
            end

            break;
        end
    end

    if iconPath ~= nil then
        -- 命中
        foodImg:loadTexture(iconPath);
        foodImg:setScale(scale);
    else
        -- 不满足提示条件
        foodNode:setVisible(false);
    end
end

-- 更新头像
function UITalentMain:updateHeadIcon(classId)
    local child = self.childList[classId];
    if not child then
        return;
    end

    local headImg = child:getChildByName("icon");
    local pet = PetM.getMyPet(classId);
    local modelId = pet:getModelId();
    local iconPath = getHeroIconPath(PetM.queryModel(modelId, "icon"));
    headImg:loadTexture(iconPath);
end

-- 隐藏喂食图标
function UITalentMain:hideFoodNode(classId)
    local child = self.childList[classId];
    if not child then
        return;
    end

    local foodNode = findChildByName(child, "food_node");
    foodNode:setVisible(false);
end

-- 更新可领取奖励泡泡
function UITalentMain:updateBonusBubble()
    local hintNode = findChildByName(self.node, "BT/btn_award/hint");
    checkBlueBubbleStatus(hintNode, TalentFruitBonusM.getAvailableBonuNum());
end

-- 初始化
function UITalentMain:init()
    -- 如果玩家没有世界树果实，则隐藏 说明 领奖 按钮
    local btnAward   = findChildByName(self.node, "BT/btn_award");
    local fruitId    = 1110;

    if ItemM.getAmount(ME.user, fruitId) > 0 then
        btnAward:setVisible(true);
        self:updateBonusBubble();
    else
        btnAward:setVisible(false);
    end

    -- 显示日期
    local dayLabel = findChildByName(self.node, "container/tree_root/notice_board/day");
    local serverTime = TimeM.getServerTime() - TimeM.getTimeOffsetDifference();
    local t = os.date("*t", serverTime);
    TextStyleM.setTextStyle(dayLabel, TextStyleM.TEXT_SIZE_SMALL2, TextStyleM.TEXT_COLOR_DARK_RED, false);
    dayLabel:setString(tostring(t.day));

    -- 显示雕像特效
    local statueEffectNode = findChildByName(self.node, "container/tree_root/statue_effect");
    local function playStatueEffect()
        playEffect(statueEffectNode, 2052, -7, 3);
        performWithDelay(statueEffectNode, playStatueEffect, math.random(2, 4));
    end
    playStatueEffect();

    -- 显示阳光特效
    local sunshineNode = findChildByName(self.node, "SUNSHINE");
    playEffect(sunshineNode, 2051, 0, 0);

    -- 动态平铺背景
    self:extendBg();

    -- 显示冈布奥模型
    self:showSlimes();

    -- 显示冈布奥宝宝
    self:showGumballBaby();
end

-- 动态平布背景
function UITalentMain:extendBg()
    -- 设置滚动容器新视口的高度
    local container = findChildByName(self.node, "container");
    local w = AlignM.frameSize.width;
    local h = AlignM.frameSize.height;
    local scale = w / DESIGN_WIDTH;
    container:setContentSize(w/scale, h/scale);

    -- 获取默认树干节点
    local treeTrunk = container:getChildByName("tree_trunk");

    -- 获取激活的天赋数量
    local pets = table.keys(ME.user.pets);
    local totalCount = #pets;

    -- 计算总高度
    local pageCount = math.ceil(totalCount / PER_PAGE_SLIME_COUNT) - 1;

    local bgTreeHeight = 610 - 66;
    local totalHeight = pageCount * bgTreeHeight + 1522;

    -- 大背景的高度
    local largeBgHeight = 1036;
    if totalHeight > largeBgHeight then
        -- 1.如果大背景不够用，平铺蓝天白云背景
        local bgSkyHeight = 536;
        local bgSkyPath = "images/ui/talent/bg2.jpg";

        -- 计算需要多少张蓝天白云的背景
        local needCount = math.ceil((totalHeight - largeBgHeight)/bgSkyHeight);

        local sky = findChildByName(self.node, "SKY");
        -- 实际多加一页，支持滑动回弹
        for i = 1, needCount + 1 do
            local posY = largeBgHeight + i * bgSkyHeight - 2;
            local image = ccui.ImageView:create();
            image:loadTexture(bgSkyPath);
            image:setAnchorPoint(0.5, 1);
            image:setScale(2);
            image:setPosition(320, posY);
            image:setLocalZOrder(-i);
            sky:addChild(image);

            if posY > totalHeight then
                -- 已经铺满了
                break;
            end
        end
    end

    -- 平铺大树背景, 多加一页，支持滑动回弹
    for i = 1, pageCount + 1 do
        local bgPath= "images/ui/talent/bg3.png";

        local image = ccui.ImageView:create();
        image:loadTexture(bgPath);
        image:setName("tree_trunk" .. i);
        image:setAnchorPoint(0.5, 0);

        local posY = i * bgTreeHeight;
        image:setPosition(treeTrunk:getPositionX(), treeTrunk:getPositionY() + posY);

        container:addChild(image);
    end

    -- 设置内部滚动的高度
    container:setInnerContainerSize(cc.size(container:getInnerContainerSize().width, totalHeight));

    -- 为了支持滑动回弹，上下不可见区域补上底图
    if self.bottomBoundaryImg == nil then
        self.bottomBoundaryImg = ccui.ImageView:create();
        self.bottomBoundaryImg:loadTexture("images/ui/talent/bottom_boundary.png");
        self.bottomBoundaryImg:setAnchorPoint(cc.p(0, 1));
        self.bottomBoundaryImg:setPositionY(2);
        container:addChild(self.bottomBoundaryImg);
    end

end

-- 设置冈布奥模型显示开关
function UITalentMain:setSlimeVisible(isVisible)
    if isVisible == true then
        self.slimeParent:setVisible(true);
    else
        self.slimeParent:setVisible(false);
    end
end

-- 显示冈布奥模型
function UITalentMain:showSlimes()
    -- 获取激活的天赋数量
    local pets = table.keys(ME.user.pets);

    -- 如果冈布奥大于一页需要加载动画
    if #pets > PER_PAGE_SLIME_COUNT then
        -- 加载动画
        showCommunicatingForm(getLocStr("loading"));
    end

    local iconParent = findChildByName(self.node, "container/icon_node");
    iconParent:setLocalZOrder(1);
    iconParent:removeAllChildren();
    self.slimeParent = iconParent;

    -- 打开冈布奥模型显示开关
    self:setSlimeVisible(true);

    local item = cc.CSLoader:createNode("layout/talent/TalentItem.csb");
    local widget = replaceNodeWithWidget(item);

    self.childList = {};

    local modelCount = 0;

    for i, classId in pairs(pets) do
        local position = TalentM.getPosition(i);
        local child = widget:clone();
        child:setVisible(false);

        -- 越下面冈布奥层级越高，防止发言被遮挡
        child:setLocalZOrder(self.petLocZOrder - i);

        -- TODO 随机延迟一下，避免一次性创建太多模型造成卡顿
        local delay;
        if i <= PER_PAGE_SLIME_COUNT then
            -- 不足一页时无需延迟
            delay = 0;
        else
            -- 超过一页时，随机延迟
            delay = math.random(1, 10)/10;
        end

        local function callback()
            child:setVisible(true);
            self:initItem(child, classId);
            modelCount = modelCount + 1;
            if modelCount == #pets then
                -- 去掉加载动画
                closeCommunicatingForm();
            end
        end
        performWithDelay(child, callback, delay);

        child:setPosition(cc.p(tonumber(position[1]), tonumber(position[2])));
        iconParent:addChild(child);
    end
end

-- 前景滑动时，处理近景、中景和远景的移动
function UITalentMain:handleBackgroundScroll()
    local middle = findChildByName(self.node, "MIDDLE");
    local far = findChildByName(self.node, "FAR");
    local sky = findChildByName(self.node, "SKY");

    -- 背景层
    local layers = { middle, far, sky };

    -- 移动的比例因子
    local factors = { 0.06, 0.05, 0.03 };

    -- 获取世界树滑动的距离
    local container = findChildByName(self.node, "container");
    local offset = container:getInnerContainer():getPositionY();

    -- 获取移动位移
    local function getOffsetY(i)
        local offset = offset * factors[i] * 6;
        if offset < 0 then
            return - math.abs(offset)/3 - math.sqrt(math.abs(offset));
        else
            return offset/3 + math.sqrt(offset);
        end
    end

    -- 背景层移动
    for i = 1, #layers do
        local layer = layers[i];
        local offsetY = getOffsetY(i);
        layer:setPositionY(self.layerPosYList[i] + offset * factors[i] * 6);
    end
end

-- 检查训练室显示
function UITalentMain:checkTrainingRoom()
    local level = TrainingM.getLevel();
    if level > 0 then
        -- 标记英雄之心标记已提醒
        if FeatureItemM.isFeatureNeedReminder("heart") then
            FeatureItemM.markAsReminded("heart");
        end

        self.trainingRoom:setVisible(true);

        -- 根据等级显示星星
        for i = 1, 6 do
            local star = findChildByName(self.trainingRoom, "star"..i);
            if i <= level + 1 then
                star:setVisible(true);
            else
                star:setVisible(false);
            end
        end
    else
        self.trainingRoom:setVisible(false);
    end

    -- 如果英雄可以升阶的话，显示泡泡
    local bubble = findChildByName(self.node, "container/tree_root/bubble");
    bubble:setVisible(false);
    bubble:setScale(1);

    local icon = findChildByName(bubble, "icon");
    local heroIcon = findChildByName(bubble, "hero_icon");
    heroIcon:setScale(1);
    heroIcon:setVisible(true);
    local modelNode = findChildByName(bubble, "model_node");
    modelNode:setVisible(true);
    modelNode:setScale(1);
    local finishStatusLabel = findChildByName(bubble, "finish_status");
    finishStatusLabel:setVisible(false);
    local upgradeStatusLabel = findChildByName(bubble, "upgrading_status");
    upgradeStatusLabel:setVisible(false);

    -- 设置描边为2像素，黑色
    TextStyleM.setOutlineStyle(finishStatusLabel, TextStyleM.TEXT_OUTLINE_SIZE, TextStyleM.TEXT_OUTLINE_COLOR_DARK);

    TextStyleM.setTextStyle(finishStatusLabel, TextStyleM.TEXT_SIZE_SMALL2, TextStyleM.TEXT_COLOR_BRIGHT_YELLOW, true, -2, 1.2);

    -- 投影为 y轴方向2像素
    finishStatusLabel:enableShadow(TextStyleM.TEXT_OUTLINE_COLOR_DARK, cc.size(0, -2));

    -- 设置描边为2像素，黑色
    TextStyleM.setOutlineStyle(upgradeStatusLabel, TextStyleM.TEXT_OUTLINE_SIZE, TextStyleM.TEXT_OUTLINE_COLOR_DARK);

    TextStyleM.setTextStyle(upgradeStatusLabel, TextStyleM.TEXT_SIZE_SMALL2, TextStyleM.TEXT_COLOR_BRIGHT_YELLOW, true, -2, 1.2);

    -- 投影为 y轴方向2像素
    upgradeStatusLabel:enableShadow(TextStyleM.TEXT_OUTLINE_COLOR_DARK, cc.size(0, -2));

    if TrainingM.getState() ~= TrainingM.TRAINING_STATE_PREPARE then
        -- 有英雄在升阶
        bubble:setVisible(true);

        -- 气泡轻微收缩
        bubble:stopAllActions();
        playBubbleSlightAnima(bubble);

        icon:setVisible(false);

        modelNode:removeAllChildren();

        local heroId = TrainingM.getHeroId();
        local pet = PetM.getMyPet(heroId);
        heroIcon:loadTexture(getHeroIconPath(pet:getIcon()));
        heroIcon:setScale(0.7);

        local msg;
        if TrainingM.getState() == TrainingM.TRAINING_STATE_FINISH then
            if self.tickNode then
                self.tickNode:stopAllActions();
            end

            msg = getLocStr("slime_upgrade_finish");
            finishStatusLabel:setVisible(true);
            finishStatusLabel:setString(msg);

            -- 文字跳动
            setJumpStop("talentMain", true);
            local nameCount = string.getWCharCount(finishStatusLabel:getString());
            setTextJump(nameCount, finishStatusLabel, self.node, "talentMain");
        else
            -- 显示倒计时
            upgradeStatusLabel:setVisible(true);
            self:startTick(upgradeStatusLabel);
        end
    elseif TrainingM.canHeroUpgrade() or
        (FeatureItemM.isFeatureOpen("heart") and FeatureItemM.isFeatureNeedReminder("heart")) then
        -- 没有英雄在升阶，但是有英雄已经满canSignIn足条件，出感叹号泡泡提示
        -- 树洞训练室取得英雄之心后，也要出泡泡提示玩家建造

        bubble:stopAllActions();
        bubble:setVisible(true);

        icon:setVisible(true);

        heroIcon:setVisible(false);
        modelNode:removeAllChildren();
        modelNode:setVisible(false);

        playBubbleAnima(bubble, true, 0.88);
    else
        -- 没有英雄在升阶，且没有满足条件的，隐藏泡泡
        bubble:stopAllActions();
        bubble:setVisible(false);
    end
end

-- 检查签到板显示
function UITalentMain:checkNoticeBoard()
    local noticeBoardBuddle = findChildByName(self.node, "container/tree_root/notice_board/notice_board_bubble");

    if NewSignInM.canSignIn() then
        noticeBoardBuddle:setVisible(true);

        -- 气泡轻微收缩
        noticeBoardBuddle:stopAllActions();
        playBubbleAnima(noticeBoardBuddle, true, 0.88);
    else
        noticeBoardBuddle:setVisible(false);
    end
end

-- 显示训练室升级特效
function UITalentMain:showTrainingRoomUpgradeEffect()
    playEffect(self.trainingRoom, 24, 120, 20);
end

-- 显示倒计时
function UITalentMain:startTick(upgradeStatusLabel)
    if self.tickNode == nil then
        self.tickNode = cc.Node:create();
        self:addChild(self.tickNode);
    end

    self.tickNode:stopAllActions();


    local function tick()
        -- 当前剩余时间
        local finishTime = TrainingM.getFinishTime();
        local serverTime = TimeM.getServerTime();
        local leftTime = finishTime - serverTime;
        if leftTime < 0 then
            leftTime = 0;
        end

        local delay = 0.5;
        if leftTime < 3600 then
            local hours = math.floor(leftTime / 3600);
            local mins =  math.floor((leftTime % 3600) / 60);
            local secs =  (leftTime % 3600) % 60;

            local desc = string.format("%02d:%02d:%02d", hours, mins, secs);
            upgradeStatusLabel:setString(desc);
            delay = 0.5;
        else
            local hours = math.floor(leftTime / 3600);
            upgradeStatusLabel:setString(hours.."h");
            if hours > 1 then
                delay = 60;
            else
                delay = 1;
            end
        end

        if leftTime > 0 then
            performWithDelay(self.tickNode, tick, delay);
        else
            self:checkTrainingRoom();
        end
    end

    tick();
end

resize = function(self)
    local w = AlignM.frameSize.width;
    local h = AlignM.frameSize.height;

    -- BT底部居中并缩放到合适大小
    AlignM.alignToBottomCenter(self.node, "BT");

    -- TOP顶部居中并缩放到合适大小
    AlignM.alignToTopCenter(self.node, "TOP");

    local function fitScreen(name)
        local node = findChildByName(self.node, name);
        node:setPosition(0, 0);
        node:setScale(w / DESIGN_WIDTH);
    end

    -- 各背景层适配
    fitScreen("container");
    fitScreen("MIDDLE");
    fitScreen("FAR");
    fitScreen("SKY");

    -- 阳光层适配
    local sunShineNode = findChildByName(self.node, "SUNSHINE");
    sunShineNode:setPosition(w, h);
    sunShineNode:setScale(w / DESIGN_WIDTH);
end

-- 底部BT按钮显示
function UITalentMain:showButtons()
    local btNode = findChildByName(self.node, "BT");
    btNode:setVisible(true);
end

 -- 底部BT按钮隐藏
function UITalentMain:hideButtons()
    local btNode = findChildByName(self.node, "BT");
    btNode:setVisible(false);
end

-- 判断是否可以随机发言
function UITalentMain:checkCanTalk(classId)
    local checkTable = { "medal", "weapon", "food", };

   local canOperate = false;
   for _, v in pairs(checkTable) do
        -- 能否操作
       if v == "food" then
            canOperate = PetM.canFeed(classId);
       else
            canOperate = PetM.canOperate(classId, v);
       end

       if canOperate == true then
            break;
       end
   end

   if not canOperate and
        not TrainingM.isUpgrading(classId) and
        not AwakeM.isCarving(classId) then
        canOperate = true;
   else
        canOperate = false;
   end

   return canOperate;
end

-- 检查冈布奥是否在可见区域内
function UITalentMain:checkEffects()
    if self:isVisible() then
        for classId, child in pairs(self.childList) do
            local node = child;
            if not isEffectNodeVisible(node) then
                node:setVisible(false);
            else
                node:setVisible(true);
            end
        end
    end
end

-- 关于下部飞入效果的初始化
function UITalentMain:setInit()
    self.flying = false;

    local BT = findChildByName(self.node, "BT");
    self.btInitPosition = BT:getPositionY();
end

-- 失去焦点,隐藏Bottom
function UITalentMain:hideBT()
    local bottom = findChildByName(self.node, "BT");
    if bottom then
        bottom:setVisible(false);
    end
end

-- 获得焦点隐藏焦点
function UITalentMain:btFlyUp()
    local bottom = findChildByName(self.node, "BT");

    -- 动作保护
    bottom:stopAllActions();
    bottom:setVisible(false);
    bottom:setPosition(bottom:getPositionX(), self.btInitPosition);

    self.flying = true;

    local function callback()
        self.flying = false;
    end

    if bottom then
        bottom:setVisible(true);

        FormEffectM.btPopUpFromBottom(self, callback);
    end
end

-- 显示冈布奥宝宝
function UITalentMain:showGumballBaby()
    local babyNode = findChildByName(self.node, "container/tree_root/baby");
    babyNode:setVisible(true);
    babyNode:removeAllChildren();

    self.babyList = {};

    local ids = FateChainM.myFateBabyList();
    -- TODO 测试位置用的
    --ids = {401,402,403,404,405,406,407,408};

    for index, id in ipairs(ids) do
        local info = FateChainM.queryBaby(id);
        local image = ccui.ImageView:create(getFateChainBabyIconPath(info["icon"]));
        image:setPosition(cc.p(info["pos"][1], info["pos"][2]));
        babyNode:addChild(image);
        image.info = info;
        table.insert(self.babyList, image);

        -- 点击宝宝
        local function onClickBaby(sender, eventType)
            if eventType == ccui.TouchEventType.ended then
                showBonusDesc({"baby", id, 1});
            end
        end
        image:addTouchEventListener(onClickBaby);
        image:setTouchEnabled(true);
    end

    self:gumballBabyJump();
end

-- 冈布奥宝宝跳跃
function UITalentMain:gumballBabyJump()
    local totalNum = #self.babyList;
    local babyNode = findChildByName(self.node, "container/tree_root/baby");

    if totalNum <= 0 then
        return;
    end
    -- 合图
    cc.SpriteFrameCache:getInstance():addSpriteFrames("images/ui/workshop_workers.plist");

    -- 创建阴影
    for i = 1, totalNum do
        -- 宝宝图片
        local babyIcon = self.babyList[i];
        local info = babyIcon.info;

        local shadow = ccui.ImageView:create();
        shadow:loadTexture("images/ui/workshop/shadow2.png", 1);
        shadow:setScale(0.8);
        babyNode:addChild(shadow);
        shadow:setLocalZOrder(-1);
        shadow:setPosition(cc.p(info["pos"][1], info["pos"][2] +8));

        babyIcon.shadow = shadow;
        shadow:setVisible(true);
    end

    for i = 1, totalNum do
        -- 找到父节点
        local babyIcon = self.babyList[i];

        -- 播放跳跃动画
        slimeJumpAnimation(babyIcon, cc.p(-3, 3), cc.p(-3, 3), cc.p(300, 800), cc.p(5, 15), babyIcon.shadow, {0,8});
    end
end
